#include "PCOMPGParser.h"

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <set>
#include <string>
#include <vector>

#include "boost/algorithm/string.hpp"

#include "Utility/Exception/SGException.h"
#include "Utility/Logging/SGLogger.h"

#include "DBManager/DBServiceFactory.h"
#include "DBManager/IService/Input/Property/IPCOMPGService.h"

#include "Import/BDFImport/BDFParserBase.h"

BULKDATA_ENTRY_PARSER_REG (PCOMPG);

using namespace SG::DBManager;
using namespace SG::Import;
using namespace SG::Import::BDF;
using namespace SG::DataStructure;
using namespace SG::DataStructure::FEM;

enum PCOMPGIndex : std::uint8_t
{
    PCOMPGIndex_PID   = 1,
    PCOMPGIndex_Z0    = 2,
    PCOMPGIndex_NSM   = 3,
    PCOMPGIndex_SB    = 4,
    PCOMPGIndex_FT    = 5,
    PCOMPGIndex_TREF  = 6,
    PCOMPGIndex_GE    = 7,
    PCOMPGIndex_MICRO = 8,
};

enum PCOMGPLayIndex : std::uint8_t
{
    PCOMGPLayIndex_GPLYID = 0,
    PCOMGPLayIndex_MID    = 1,
    PCOMGPLayIndex_THICK  = 2,
    PCOMGPLayIndex_THETA  = 3,
    PCOMGPLayIndex_SOUT   = 4,
    PCOMGPLayIndex_GEFLG  = 5,  // 只在首层出现
};

namespace
{
    // 解析PCOMPG主卡片数据
    void parseGeneralData (std::vector<std::string>& dividedEntryInfo, PCOMPGData& tempData)
    {
        for (int curIndex = 1; curIndex <= 8; ++curIndex)
        {
            std::string curStrKey = dividedEntryInfo[curIndex];
            bool        isEmpty   = is_all_space (curStrKey);
            if (isEmpty)
                continue;

            switch (curIndex)
            {
            case PCOMPGIndex_PID:
                tempData.m_id = convertToI64 (curStrKey);
                break;
            case PCOMPGIndex_Z0:
                tempData.m_Z0 = convertToReal (curStrKey);
                break;
            case PCOMPGIndex_NSM:
                tempData.m_NSM = convertToReal (curStrKey);
                break;
            case PCOMPGIndex_SB:
                tempData.m_SB = convertToReal (curStrKey);
                break;
            case PCOMPGIndex_FT:
                boost::trim (curStrKey);
                if ("HILL" == curStrKey)
                    tempData.m_FT = 1;
                else if ("HOFF" == curStrKey)
                    tempData.m_FT = 2;
                else if ("TSAI" == curStrKey)
                    tempData.m_FT = 3;
                else if ("STRN" == curStrKey)
                    tempData.m_FT = 4;
                else if ("HFAIL" == curStrKey)
                    tempData.m_FT = 5;
                else if ("HTAPE" == curStrKey)
                    tempData.m_FT = 6;
                else if ("HFABR" == curStrKey)
                    tempData.m_FT = 7;
                break;
            case PCOMPGIndex_TREF:
                tempData.m_TREF = convertToReal (curStrKey);
                break;
            case PCOMPGIndex_GE:
                tempData.m_GE = convertToReal (curStrKey);
                break;
            case PCOMPGIndex_MICRO:
                // 填写MICRO，如有必要
                strncpy (tempData.m_MICRO, curStrKey.c_str (), sizeof (tempData.m_MICRO) - 1);
                break;
            default:
                break;
            }
        }
    }

    // 判断铺层是否存在（Remark 3）
    bool plyFieldExists (const bool recordEmpty[6])
    {
        // 必须有GPLYID，且 MID/THICK/THETA/SOUT 中至少有一个非空
        if (recordEmpty[PCOMGPLayIndex_GPLYID])
            return false;
        if (!recordEmpty[PCOMGPLayIndex_MID] || !recordEmpty[PCOMGPLayIndex_THICK] || !recordEmpty[PCOMGPLayIndex_THETA]
            || !recordEmpty[PCOMGPLayIndex_SOUT])
            return true;
        return false;
    }

    // 解析单层数据
    void parseSingleLayData (std::vector<std::string>& dividedEntryInfo, size_t baseIndex, PCOMPGData& tempData)
    {
        bool   recordEmpty[6] = { false };
        size_t recordIndex[6] = { 0 };

        for (size_t i = 0; i < 6; i++)
        {
            recordIndex[i] = baseIndex + i + 1;
            if (recordIndex[i] >= dividedEntryInfo.size ())
            {
                recordEmpty[i] = true;
                continue;
            }
            recordEmpty[i] = std::all_of (
                dividedEntryInfo[recordIndex[i]].begin (), dividedEntryInfo[recordIndex[i]].end (), [] (char c) { return c == ' '; });
        }

        if (!plyFieldExists (recordEmpty))  // Remark 3
            throw SG::Utility::ParserError ("PCOMPG " + std::to_string (tempData.m_id) + "has no valid layers defined.");

        PCOMPGData::PLYData ply;

        // GPLYID
        ply.m_GPLYID = convertToI64 (dividedEntryInfo[recordIndex[PCOMGPLayIndex_GPLYID]]);

        // MID
        if (recordEmpty[PCOMGPLayIndex_MID])
        {
            if (tempData.m_PLYList.empty ())
            {
                SGError << "First ply must specify MID explicitly.";
                throw SG::Utility::ParserError ("PCOMPG: First ply must have MID!");
            }
            else
            {
                ply.m_MID = tempData.m_PLYList.back ().m_MID;
            }
        }
        else
        {
            ply.m_MID = convertToI64 (dividedEntryInfo[recordIndex[PCOMGPLayIndex_MID]]);
        }

        // THICK
        if (recordEmpty[PCOMGPLayIndex_THICK])
        {
            if (tempData.m_PLYList.empty ())
            {
                SGError << "First ply must specify THICK explicitly.";
                throw SG::Utility::ParserError ("PCOMPG: First ply must have THICK!");
            }
            else
            {
                ply.m_THICK = tempData.m_PLYList.back ().m_THICK;
            }
        }
        else
        {
            ply.m_THICK = convertToReal (dividedEntryInfo[recordIndex[PCOMGPLayIndex_THICK]]);
        }

        // THETA
        if (recordEmpty[PCOMGPLayIndex_THETA])
            ply.m_THETA = 0.0;
        else
            ply.m_THETA = convertToReal (dividedEntryInfo[recordIndex[PCOMGPLayIndex_THETA]]);

        // SOUT
        if (recordEmpty[PCOMGPLayIndex_SOUT])
        {
            ply.m_SOUT = 0;
        }
        else
        {
            boost::trim (dividedEntryInfo[recordIndex[PCOMGPLayIndex_SOUT]]);
            if ("YES" == dividedEntryInfo[recordIndex[PCOMGPLayIndex_SOUT]])
                ply.m_SOUT = 1;
            else
                ply.m_SOUT = 0;
        }

        // GEFLG 只在第一层
        if (!recordEmpty[PCOMGPLayIndex_GEFLG] && tempData.m_PLYList.empty ())
            ply.m_GEFLG = convertToI64 (dividedEntryInfo[recordIndex[PCOMGPLayIndex_GEFLG]]);

        tempData.m_PLYList.push_back (ply);
    }

    // 解析所有层数据
    void parseLayData (std::vector<std::string>& dividedEntryInfo, PCOMPGData& tempData)
    {
        // 每层最多6个字段，从第9个字段（下标8）开始
        size_t plyStart      = 10;
        size_t plyFieldCount = 10;  // 每层数据包含10个字段
        for (size_t idx = plyStart; idx + 1 < dividedEntryInfo.size (); idx += plyFieldCount)
        {
            parseSingleLayData (dividedEntryInfo, idx, tempData);
        }
    }
}  // namespace

SG::DataStructure::Common::Status BDF::BULKDATA::ParsePCOMPG (const std::shared_ptr<SG::DBManager::DBServiceFactory>& dbServiceFactory,
                                                              std::vector<std::string>&                               dividedEntryInfo)
{
    PCOMPGData tempData;
    tempData.m_domainId = 1;

    parseGeneralData (dividedEntryInfo, tempData);
    parseLayData (dividedEntryInfo, tempData);

    if (tempData.m_PLYList.empty ())
    {
        throw SG::Utility::ParserError ("PCOMPG " + std::to_string (tempData.m_id) + "has no valid layers defined.");
    }

    // Z0 缺省自动填充 Remark 11
    bool isEmpty =
        std::all_of (dividedEntryInfo[PCOMPGIndex_Z0].begin (), dividedEntryInfo[PCOMPGIndex_Z0].end (), [] (char c) { return c == ' '; });
    if (isEmpty)
    {
        tempData.m_Z0 = 0;
        for (const auto& ply : tempData.m_PLYList)
            tempData.m_Z0 += ply.m_THICK;
        tempData.m_Z0 *= -0.5;
    }

    // NPLIES自动统计
    tempData.m_NPLIES = static_cast<SG::DataStructure::Common::I64> (tempData.m_PLYList.size ());

    // Remark 1: 确保GPLYID唯一
    std::set<SG::DataStructure::Common::I64> gplyid_set;
    for (const auto& ply : tempData.m_PLYList)
    {
        if (!gplyid_set.insert (ply.m_GPLYID).second)
        {
            SGError << "Duplicate GPLYID found: " << ply.m_GPLYID;
            return SG::DataStructure::Common::Status::STATUS_Failure;
        }
    }

    // Remark 4/5: TREF、GE 只取PCOMPG卡上的，已在 parseGeneralData 实现

    // 其它Remark如SMEAR/SMCORE、失效参数、Case Control等在后处理或求解流程判断

    // 写入数据库
    return dbServiceFactory->get<IPCOMPGService> ()->Add (tempData);
}